package com.gzstv.dlnasdk;

import com.gzstv.dlnasdk.server.LocalContentServer;
import com.gzstv.dlnasdk.upnp.ControlPoint;
import com.gzstv.dlnasdk.upnp.Device;
import com.gzstv.dlnasdk.upnp.SSDPClient;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class DLNAManager implements SSDPClient.SSDPResponseListener {

    private static DLNAManager instance;
    private SSDPClient ssdpClient;
    private List<Device> discoveredDevices;
    private List<DeviceDiscoveryListener> listeners;
    private Device selectedDevice;
    private LocalContentServer localContentServer;
    private ExecutorService networkExecutor;
    private DLNAManagerCallback callback;

    public interface DeviceDiscoveryListener {
        void onDeviceDiscovered(Device device);
        void onDeviceRemoved(Device device);
        void onDiscoveryFinished();
    }

    public interface DLNAManagerCallback {
        void onActionSuccess(String action);
        void onActionFailed(String action, String errorMessage);
    }

    private DLNAManager() {
        discoveredDevices = new CopyOnWriteArrayList<>();
        listeners = new CopyOnWriteArrayList<>();
        ssdpClient = new SSDPClient(this);
        localContentServer = new LocalContentServer();
        networkExecutor = Executors.newSingleThreadExecutor(); // Use a single thread for network operations
    }

    public static synchronized DLNAManager getInstance() {
        if (instance == null) {
            instance = new DLNAManager();
        }
        return instance;
    }

    public void setCallback(DLNAManagerCallback callback) {
        this.callback = callback;
    }

    public void startSearch() {
        discoveredDevices.clear();
        ssdpClient.startDiscovery();
    }

    public void stopSearch() {
        ssdpClient.stopDiscovery();
    }

    public List<Device> getDiscoveredDevices() {
        return discoveredDevices;
    }

    public void registerDeviceDiscoveryListener(DeviceDiscoveryListener listener) {
        if (listener != null && !listeners.contains(listener)) {
            listeners.add(listener);
        }
    }

    public void unregisterDeviceDiscoveryListener(DeviceDiscoveryListener listener) {
        if (listener != null) {
            listeners.remove(listener);
        }
    }

    @Override
    public void onDeviceDiscovered(Device device) {
        // UDN is the unique identifier for a device
        if (device.getUdn() == null || device.getUdn().isEmpty()) {
            return; // Ignore devices without a UDN
        }

        boolean found = false;
        for (Device existingDevice : discoveredDevices) {
            if (existingDevice.getUdn().equals(device.getUdn())) {
                found = true;
                // Optional: Update device information if needed, e.g., location URL changed
                break;
            }
        }

        if (!found) {
            discoveredDevices.add(device);
            for (DeviceDiscoveryListener listener : listeners) {
                listener.onDeviceDiscovered(device);
            }
        }
    }

    public void setSelectedDevice(Device device) {
        this.selectedDevice = device;
    }

    public Device getSelectedDevice() {
        return selectedDevice;
    }

    public void cast(String url, String metaData) {
        if (selectedDevice == null) {
            if (callback != null) {
                callback.onActionFailed("cast", "No device selected for casting.");
            }
            return;
        }

        networkExecutor.execute(() -> {
            String finalUrl = url;
            String finalMetaData = metaData;
            try {
                // Check if it's a local file path
                if (url.startsWith("/") || url.startsWith("file://")) {
                    File file = new File(url.replace("file://", ""));
                    if (file.exists() && file.isFile()) {
                        finalUrl = localContentServer.startServer(file);
                    } else {
                        throw new IOException("Local file not found: " + url);
                    }
                } else if (url.contains(".m3u8")) {
                    // For HLS (m3u8) streams, we need to provide specific metadata
                    finalMetaData = createHlsMetaData(url);
                }

                ControlPoint.setAVTransportURI(selectedDevice, finalUrl, finalMetaData);
                ControlPoint.play(selectedDevice);
                if (callback != null) {
                    callback.onActionSuccess("cast");
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (callback != null) {
                    callback.onActionFailed("cast", e.getMessage());
                }
            }
        });
    }

    private String createHlsMetaData(String url) {
        String meta = "<DIDL-Lite " +
                "xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\" " +
                "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" " +
                "xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\">" +
                "<item id=\"0\" parentID=\"-1\" restricted=\"1\">" +
                "<dc:title>HLS Stream</dc:title>" +
                "<upnp:class>object.item.videoItem.movie</upnp:class>" +
                "<res protocolInfo=\"http-get:*:application/vnd.apple.mpegurl:*\">" + url + "</res>" +
                "</item>" +
                "</DIDL-Lite>";
        return meta;
    }

    public void play() {
        if (selectedDevice == null) {
            if (callback != null) {
                callback.onActionFailed("play", "No device selected for playback.");
            }
            return;
        }
        networkExecutor.execute(() -> {
            try {
                ControlPoint.play(selectedDevice);
                if (callback != null) {
                    callback.onActionSuccess("play");
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (callback != null) {
                    callback.onActionFailed("play", e.getMessage());
                }
            }
        });
    }

    public void pause() {
        if (selectedDevice == null) {
            if (callback != null) {
                callback.onActionFailed("pause", "No device selected for pause.");
            }
            return;
        }
        networkExecutor.execute(() -> {
            try {
                ControlPoint.pause(selectedDevice);
                if (callback != null) {
                    callback.onActionSuccess("pause");
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (callback != null) {
                    callback.onActionFailed("pause", e.getMessage());
                }
            }
        });
    }

    public void stop() {
        if (selectedDevice == null) {
            if (callback != null) {
                callback.onActionFailed("stop", "No device selected for stop.");
            }
            return;
        }
        networkExecutor.execute(() -> {
            try {
                ControlPoint.stop(selectedDevice);
                localContentServer.stopServer(); // Stop local server when casting stops
                if (callback != null) {
                    callback.onActionSuccess("stop");
                }
            } catch (IOException e) {
                e.printStackTrace();
                if (callback != null) {
                    callback.onActionFailed("stop", e.getMessage());
                }
            }
        });
    }

    public void stopLocalServer() {
        localContentServer.stopServer();
    }

    // TODO: Implement onDeviceRemoved and onDiscoveryFinished if needed for more advanced discovery
}